#include "sys/Memory.h"
#include "NOW.h"
#include "sys/IO.h"
#include "sys/TLB.h"
#define INIT_PAGE 10
#define MIN_PAGE 4
#define MAX_PF_RATE 0.1
#define MIN_PF_RATE 0.001

static void Log(const char *format, ...){
    va_list args;
    va_start(args, format);          // 初始化va_list
    vfprintf(stdout,format, args);  // 将参数传递给vfprintf
    va_end(args);   
}
static void LogErr(const char *format, ...) {
    va_list args;
    va_start(args, format);          // 初始化va_list
    vfprintf(stderr, format, args);  // 将参数传递给vfprintf
    va_end(args);                    // 清理va_list
}
//MIN_PF_RATE生效仅在当前物理内存不够分配下一个进程
int freepages = PHY_PAGE_NUM;

//强制删除指定的一块frame，proc记录的分配帧数,影响TLB，页表所有指向其的项。返回值0表示目标为空闲块无需释放，1表示成功释放。
int release_frame(unsigned ppn, int T){
    int owner = frame_table[ppn].pid;
    if (owner == -1) return 0;
    --jobs[owner].allocated_frames;
    frame_table[ppn].pid = -1;
    ++freepages;
    //清除TLB
    TLB_delF(ppn,T);
    //清除页表项
    VPI *refSrc = frame_table[owner].refSrc;
    while (refSrc){
        VPI target = pop_frame_refSrc(&refSrc);
        jobs[target.pid].PTB->e[target.VPN].v = 0;
    }
    //不必彻底清除frame_table;
    return 1;
}
//清空再重新分配某frame（包含对proc->allocated_frames相关处理），若需要进行清空则返回1，否则返回0
int allocate_frame(unsigned ppn, FrameEntry fe, int T){
    Process *proc = &jobs[fe.pid];
    int res = release_frame(ppn, T);
    --freepages;
    frame_table[ppn] = fe;
    ++proc->allocated_frames;
}
int allocate_frames(int pid, int T){
    Process *proc = &jobs[pid];
    if (proc->page_find_cnt==0){//第一次分配
        Log("[%d] Process %d start being first allocated frames.\n",T,pid);
        if (freepages<MIN_PAGE){
            Log("[%d] Process %d first allocated frames fails for limited %d frames(<%d).\n",T,pid,freepages,MIN_PAGE);
            return 0;//分配失败
        } 
        else{
            int alloc_n = INIT_PAGE > freepages? INIT_PAGE : freepages;
            freepages -= alloc_n;
            int allocted = 0;
            for (int i=0;i<PHY_PAGE_NUM && allocted!=alloc_n;++i){
                if (frame_table[i].pid==-1){
                    frame_table[i].pid = pid;
                    frame_table[i].VPN = -1;
                    ++allocted;
                }
            }
            proc->allocated_frames=alloc_n;
            Log("[%d] Process %d is first allocated %d frames.\n",T,pid,alloc_n);
            return alloc_n;
        }
    }else{//动态调整
        double miss_rate = (double)proc->page_fault_cnt/proc->page_find_cnt;
        int alloc_n = 0,victim;
        if (miss_rate>=MAX_PF_RATE){//page_fault率过多，多分配一frame
            Log("[%d] Process %d start being allocated more frames for high miss rate %.lf.\n",T,pid,miss_rate);
            if (proc->page_fault_cnt <= proc->allocated_frames){
                //防止冷启动误判
                Log("[%d] Process %d has no need to allocate more memories.\n",T,pid);
                return 0;
            }
            //全局帧替换算法
            victim = victim_frame(-1,0);
            if (victim == -1){
                LogErr("[%d] frameAllocator allocates more frame for process %d failed.",T,pid);
                return 0;
            }
            FrameEntry fe; fe.pid = pid; fe.VPN = -1; fe.refSrc = NULL;fe.ref = 0;
            allocate_frame(victim,fe, T);
            Log("[%d] Process %d is successfully allocated more %d frames.\n",T,pid,alloc_n);
            return alloc_n;
        }else if (miss_rate<MIN_PF_RATE && freepages<MIN_PAGE && proc->allocated_frames>MIN_PAGE){
            Log("[%d] Process %d start being allocated less frames for low miss rate %.lf.\n",T,pid,miss_rate);
            //局部帧替换算法
            victim = victim_frame(pid,0);
            if (victim == -1){
                LogErr("[%d] frameAllocator releases a frame for process %d failed.",T,pid);
                return 0;
            }
            release_frame(victim,T);
            Log("[%d] Process %d is successfully freed %d frames.\n",T,pid,1);
            return -1;
        }
        Log("[%d] MemAllocator:Process %d with miss rate %.lf has no need to change.\n",T,pid,miss_rate);
        return 0;
    }
}